﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Xml;
using System.Drawing;
using NeuralNetwork.OutputTypes;
using NeuralNetwork.Neurons;
using NeuralNetwork.Normalization;

namespace NeuralNetwork.Layers
{
    public class InputLayer : Layer 
    {
        private NormalizeFunction norm = null;
        
        public InputLayer(Network network)
        {
            this.network = network;
            this.ID = network.GetUniqueID();
            network.input = this;
        }

        public InputLayer(Network network, int neurons, string neurontype):this(network)
        {
            for (int i = 0; i < neurons; i++) AddNeuron(neurontype);
        }

        public NormalizeFunction Normalize
        {
            get { return norm; }
            set { norm = value; }
        }

        public List<Output> Observe(Bitmap input)
        {
            if (input.Width * input.Height != neurons.Count)
            { 
                network.log.Print("ERROR: Input dimensions does not match inputlayer size", Log.LogLevel.LOG_ERROR );
                return new List<Output>();
            }

            List<double> bmpAsList = new List<double>(input.Width * input.Height);
            double value = 0;
            double d = (double)(255 * 255 * 255 + 255 * 255 + 255);
            for (int y = 0; y < input.Height; y++)
            {
                for (int x = 0; x < input.Width; x++)
                {
                    Color c = input.GetPixel(x, y);
                    value = Convert.ToDouble((c.R * 255 * 255 + c.G * 255 + c.B)/d);
                    bmpAsList.Add(value); 
                }
            }

            return Observe(bmpAsList);
        }

        public List<Output> Observe(List<double> input)
        {
            // Validate size
            if (input.Count != neurons.Count)
            {
                network.log.Print("ERROR: Input length does not match inputlayer size", Log.LogLevel.LOG_ERROR);
                return new List<Output>();
            }

            // Normalize input
            if (norm != null)
            {
                network.log.Print("- Normalize input");
                norm.Normalize(input);
            }

            // Activate charge based on the input
            network.log.Print("- Charge network");
            int exposure = network.ObserveTime;
            while (exposure > 0)
            {
                for (int i = 0; i < neurons.Count; i++)
                {
                    neurons[i].Absorb(input[i]);
                }
                Update();
                exposure--;
            }

            return network.output.Result();
        }    
    }
}
